﻿namespace ShellNavigator.Client.Utils
{
  #region

  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  using System.Threading;

  using ShellNavigator.Client.Models;

  using FileInfo = ShellNavigator.Client.Models.FileInfo;

  #endregion

  public class DirectoryHelper
  {
    #region Public Methods

    public static IEnumerable<string> EnumerateFiles(string directory, string searchPattern, SearchOption options, CancellationToken cancelToken)
    {
      if (cancelToken.IsCancellationRequested)
      {
        return Enumerable.Empty<string>();
      }

      var results = IgnoreErrors(() => Directory.GetFiles(directory, searchPattern));

      if (cancelToken.IsCancellationRequested)
      {
        return results;
      }

      if (options.HasFlag(SearchOption.AllDirectories))
      {
        var subResults = IgnoreErrors(
          () => from subDirectory in Directory.GetDirectories(directory)
                from file in EnumerateFiles(subDirectory, searchPattern, options, cancelToken)
                select file);

        results = results.Concat(subResults.ToList());
      }

      return results;
    }

    public static IEnumerable<ProjectInfo> EnumerateProjects(FileInfo file, CancellationToken cancelToken)
    {
      var baseDirectory = Path.GetDirectoryName(file.Name);
      var shortName = Path.GetFileName(file.Name);

      const string SearchPattern = "*.??proj";
      var projects = new List<ProjectInfo>();

      while (!string.IsNullOrEmpty(baseDirectory) && !projects.Any())
      {
        var request = from enumeratedFile in EnumerateFiles(baseDirectory, SearchPattern, SearchOption.TopDirectoryOnly, cancelToken)
                      where FilterHelper.FileInProjectCondition(enumeratedFile, shortName)
                      select new ProjectInfo(file) { Name = enumeratedFile };

        projects = request.ToList();

        baseDirectory = Path.GetDirectoryName(baseDirectory);
      }

      return projects;
    }

    public static IEnumerable<SolutionInfo> EnumerateSolutions(ProjectInfo project, CancellationToken cancelToken)
    {
      var baseDirectory = Path.GetDirectoryName(project.Name);
      var shortName = Path.GetFileName(project.Name);

      const string SearchPattern = "*.sln";
      var solutions = new List<SolutionInfo>();

      while (!string.IsNullOrEmpty(baseDirectory) && !solutions.Any())
      {
        var request = from enumeratedFile in EnumerateFiles(baseDirectory, SearchPattern, SearchOption.TopDirectoryOnly, cancelToken)
                      where FilterHelper.ProjectInSolutionCondition(enumeratedFile, shortName)
                      select new SolutionInfo(project) { Name = enumeratedFile };

        solutions = request.ToList();

        baseDirectory = Path.GetDirectoryName(baseDirectory);
      }

      return solutions;
    }

    #endregion

    #region Methods

    private static IEnumerable<T> IgnoreErrors<T>(Func<IEnumerable<T>> func)
    {
      var result = Enumerable.Empty<T>();

      try
      {
        result = func();
      }
      catch (UnauthorizedAccessException)
      {
      }

      return result;
    }

    #endregion
  }
}